/**
 * @file   time.hpp
 * @author wolfhead <wolfhead@wolfhead-laptop>
 * @date   Wed Aug 24 08:42:37 2011
 * 
 * @brief  
 * 
 * 
 */
#ifndef ACAPELLA_UTIL_TIME_HPP
#define ACAPELLA_UTIL_TIME_HPP

// for strptime
#include <time.h>
#include <sys/time.h>
#include <memory.h>
#include <string>

namespace acapella
{
namespace util
{

class Time
{
public:
    typedef unsigned long long MilliSecond;
    typedef time_t TimeVal;

    static MilliSecond GetMilliSecond()
    {
        struct timeval tv;
        gettimeofday(&tv, NULL);
        return  (tv.tv_usec/1000 + tv.tv_sec*1000);
    }

    static TimeVal GetTimeVal()
    {
        return time(NULL);
    }

    static TimeVal Now()
    {
        return time(NULL);
    }


    static TimeVal GetGreenwishStart()
    {
        struct timeval tv;
        struct timezone tz;
        gettimeofday(&tv, &tz);
        return tz.tz_minuteswest * 60;
    }

    template<size_t bufSize>
    static std::string Format(const char * format, TimeVal sec = Time::GetTimeVal())
    {
        char buffer[bufSize];
        tm tmpTm;
        localtime_r(&sec, &tmpTm);
        strftime(buffer, bufSize, format, &tmpTm);
        return buffer;
    }

#ifdef _XOPEN_SOURCE
    static TimeVal Parse(const char * date, const char * format, TimeVal defaultTime = 0)
    {
        char * end = NULL;
        struct tm tm;
        memset(&tm, 0, sizeof(tm));
        
        // fail of match returns NULL
        end = strptime(date, format, &tm);
        return (end != NULL) ? mktime(&tm) : defaultTime;
    }
#endif

};


class TimeSpan
{
public:
    TimeSpan(Time::TimeVal start, Time::TimeVal end)
    {
        GetTimeSpan(start, end, this);
    }

    TimeSpan(Time::TimeVal end)
    {
        GetTimeSpan(Time::GetGreenwishStart(), end, this);
    }

    TimeSpan(const TimeSpan& span)
    {
        memcpy(this, &span, sizeof(TimeSpan));
    }

    TimeSpan& operator=(const TimeSpan& span)
    {
        if (this == &span)
        {
            return *this;
        }

        memcpy(this, &span, sizeof(TimeSpan));
        return *this;
    }

    static void GetTimeSpan(
        Time::TimeVal start, 
        Time::TimeVal end, 
        TimeSpan* span)
    {
        if (span == NULL)
        {
            return;
        }
        
        memset(span, 0, sizeof(span));
        if (start > end)
        {
            return;
        }

        span->second = end - start;
        span->minute = span->second / 60;
        span->hour = span->minute / 60;
        span->day = span->hour / 24;
    }

    int day;
    int hour;
    int minute;
    int second;
};


class Timer
{
public:
    Timer()
    {
        Reset();
    }

    void Set(Time::MilliSecond ms = 0)
    {
        begin_ = Time::GetMilliSecond();
        end_ = begin_ + ms;
    }

    void Reset()
    {
        begin_ = 0;
        end_ = 0;
    }

    Time::MilliSecond Get()
    {
        return Time::GetMilliSecond() - begin_;
    }

    bool Expired()
    {
        return end_ < Time::GetMilliSecond();
    }

private:

    Time::MilliSecond begin_;
    Time::MilliSecond end_;
};

}//namespace util
}//namespace acapella


#endif
